package org.scboy.coobi.runner;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.dubbo.config.annotation.DubboReference;
import org.scboy.coobi.caches.DbTableCache;
import org.scboy.coobi.caches.InitDataCache;
import org.scboy.coobi.caches.SqlCache;
import org.scboy.coobi.sql.Coobi;
import org.scboy.coobi.utils.CacheWriteFileUtil;
import org.scboy.coobi.init.DbInit;
import org.scboy.coobi.properties.CoobiProperties;
import org.scboy.coobi.scan.FileScan;
import org.scboy.dbms.api.IDbManager;
import org.scboy.dbms.api.entity.SqlConfig;
import org.scboy.utils.entitys.coobi.DbColumn;
import org.scboy.utils.entitys.coobi.DbIndex;
import org.scboy.utils.entitys.coobi.DbTable;
import org.scboy.utils.entitys.coobi.Model;
import org.scboy.utils.entitys.coobi.SQLConfig;
import org.scboy.utils.entitys.coobi.SQLEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import cn.hutool.core.lang.UUID;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import jakarta.annotation.PreDestroy;

@Component
public class StartupRunner implements ApplicationRunner {

	Log log=LogFactory.get();
	
	@DubboReference
	IDbManager dbManager;
	
	@Autowired
	CoobiProperties properties;
	
	@Autowired
	Coobi coobi;
	
	@Autowired
	DbInit dbInit;
	
	Timer timer=new Timer();
	
	@Override
	public void run(ApplicationArguments args) throws Exception {
		
		//初始化表
		initDbTable(true);
		
		//初始化SQL配置
		initSql();
		
		//初始化数据
		initData();
		
		timer.scheduleAtFixedRate(new TimerTask() {

			@Override
			public void run() {
				try {

					//更新服务表配置
					taskDbTable();
				} catch (Exception e) {
					log.error(e);
				}
				
				try {
					//更新服务SQL配置
					taskSql();
				} catch (Exception e) {
					log.error(e);
				}
				try {
					//缓存文件
					CacheWriteFileUtil.cacheToFile();
				} catch (Exception e) {
					log.warn("无法缓存文件",e);
				}
			}
		}, 0,properties.getTimer());
	}
	
	/**
	 * 心跳更新和处理表结构
	 */
	public void taskDbTable() {
		//获取服务器数据表配置
		List<org.scboy.dbms.api.entity.DbTable> dbTables = dbManager.taskDbEntity(properties.getServerName());
		
		for (org.scboy.dbms.api.entity.DbTable table : dbTables) {
			//如不内存中数据表配置的版本小于数据库版本，活着不存在
			DbTable dbTable=DbTableCache.getDbTable(table.getName());
			if(dbTable==null || dbTable.getVersion() < table.getVersion()) {
				dbTable=new DbTable();
				dbTable.setComment(table.getComment());
				dbTable.setName(table.getName());
				dbTable.setVersion(table.getVersion());
				
				//列转换
				List<DbColumn> columns=new ArrayList<DbColumn>();
				for (org.scboy.dbms.api.entity.DbColumn column : table.getColumns()) {
					DbColumn dbColumn=new DbColumn();
					dbColumn.setComment(column.getComment());
					dbColumn.setDbType(column.getDbType());
					dbColumn.setEntityName(column.getEntityName());
					dbColumn.setLength(column.getLength());
					dbColumn.setName(column.getName());
					dbColumn.setNotNull(column.isNotNull());
					dbColumn.setPrimary(column.isPrimary());
					columns.add(dbColumn);
				}
				dbTable.setColumns(columns);
				
				//索引转换
				List<DbIndex> indexs=new ArrayList<DbIndex>();
				for (org.scboy.dbms.api.entity.DbIndex index : table.getIndexs()) {
					DbIndex dbIndex=new DbIndex();
					dbIndex.setColumns(index.getColumns());
					dbIndex.setIndexName(index.getIndexName());
					indexs.add(dbIndex);
				}
				dbTable.setIndexs(indexs);
				
				//刷新数据库配置表缓存
				DbTableCache.setDbTable(table.getName(), dbTable);
			}
		}
		
		//重新处理表结构
		initDbTable(false);
	}
	
	/**
	 * 心跳更新和处理SQL配置
	 */
	public void taskSql() {
		//获取服务器sql配置
		List<org.scboy.dbms.api.entity.SqlConfig> sqlConfigs = dbManager.taskSql(properties.getServerName());
		for (SqlConfig config : sqlConfigs) {
			if(!SqlCache.check(config.getSqlId())) {
				
				SQLConfig sqlConfig=new SQLConfig();
				sqlConfig.setId(config.getSqlId());
				sqlConfig.setName(config.getName());
				sqlConfig.setNote(config.getNote());
				
				SQLEntity entity=new SQLEntity();
				entity.setName(config.getSqlEntity().getName());
				entity.setNote(config.getSqlEntity().getNote());
				entity.setSql(config.getSqlEntity().getSql());
				entity.setVersion(config.getSqlEntity().getVersion());
				sqlConfig.setSqlEntity(entity);
				
				//新增缓存SQL配置
				SqlCache.setConfig(sqlConfig);
				
			}else {
				if(SqlCache.getConfig(config.getSqlId()).getSqlEntity().getVersion()< config.getSqlEntity().getVersion()) {
					
					SQLEntity entity=new SQLEntity();
					entity.setName(config.getSqlEntity().getName());
					entity.setNote(config.getSqlEntity().getNote());
					entity.setSql(config.getSqlEntity().getSql());
					entity.setVersion(config.getSqlEntity().getVersion());
					
					//更新缓存SQL配置
					SqlCache.getConfig(config.getSqlId()).setSql(entity);
				}
			}
		}
		
		//重构SQL配置
		initSql();
	}
	

	/**
	 * 初始化表
	 */
	@Transactional
	public void initDbTable(boolean initState) {
		
		//先只初始化未创建的表，在表未创建之前，不能存储表配置数据。
		for (DbTable table : DbTableCache.getDbTables()) {
			//检查表是否存在
			if (!coobi.exists(table.getName(),null)) {
				log.info("新增数据库表["+table.getName()+"]表。");
				if (!coobi.createTable(table)) {
					throw new RuntimeException("创建数据库表["+table.getName()+"]表失败。");
				}
				//创建索引
				for (DbIndex index : table.getIndexs()) {
					if(!coobi.createIndex(table.getName(), index)) {
						throw new RuntimeException("创建数据库表["+table.getName()+"]的["+index.getIndexName()+"]索引失败。");
					}
				}
			}
		}
		
		//更新表，并且存储配置数据
		for (DbTable table : DbTableCache.getDbTables()) {
			
			//转换表
			org.scboy.dbms.api.entity.DbTable dbTable=new org.scboy.dbms.api.entity.DbTable();
			dbTable.setId(UUID.randomUUID().toString());
			dbTable.setServerName(properties.getServerName());
			dbTable.setComment(table.getComment());
			dbTable.setName(table.getName());
			dbTable.setVersion(table.getVersion());
			
			//转换列
			List<org.scboy.dbms.api.entity.DbColumn> columns=new ArrayList<org.scboy.dbms.api.entity.DbColumn>();
			for (DbColumn column : table.getColumns()) {
				org.scboy.dbms.api.entity.DbColumn dbColumn=new org.scboy.dbms.api.entity.DbColumn();
				dbColumn.setId(UUID.randomUUID().toString());
				dbColumn.setTableId(dbTable.getId());
				dbColumn.setComment(column.getComment());
				dbColumn.setDbType(column.getDbType());
				dbColumn.setEntityName(column.getEntityName());
				dbColumn.setLength(column.getLength());
				dbColumn.setName(column.getName());
				dbColumn.setNotNull(column.isNotNull());
				dbColumn.setPrimary(column.isPrimary());
				columns.add(dbColumn);
			}
			dbTable.setColumns(columns);
			
			//转换索引
			List<org.scboy.dbms.api.entity.DbIndex> indexs=new ArrayList<org.scboy.dbms.api.entity.DbIndex>();
			for (DbIndex index : table.getIndexs()) {
				org.scboy.dbms.api.entity.DbIndex dbIndex=new org.scboy.dbms.api.entity.DbIndex();
				dbIndex.setId(UUID.randomUUID().toString());
				dbIndex.setTableId(dbTable.getId());
				dbIndex.setColumns(index.getColumns());
				dbIndex.setIndexName(index.getIndexName());
				indexs.add(dbIndex);
			}
			dbTable.setIndexs(indexs);
			
			//查询数据库配置表配置信息
			org.scboy.dbms.api.entity.DbTable oldTable=dbManager.getTable(properties.getServerName(),table.getName());
			if(oldTable==null) {
				dbManager.insertTable(dbTable);
				
				//新增列配置
				for (org.scboy.dbms.api.entity.DbColumn column : dbTable.getColumns()) {
					dbManager.insertColumn(column);
				}
				
				//新增索引配置
				for (org.scboy.dbms.api.entity.DbIndex index : dbTable.getIndexs()) {
					dbManager.insertIndex(index);
				}
				
				oldTable=dbTable;
			}
			
			if((initState && oldTable.getVersion() < dbTable.getVersion()) || (!initState && oldTable.getVersion() > dbTable.getVersion())) {
				dbTable.setId(oldTable.getId());
				dbManager.updateTable(dbTable);
				
				//删除列配置
				dbManager.deleteColumns(oldTable.getId());
				
				//新增列配置
				for (org.scboy.dbms.api.entity.DbColumn column : dbTable.getColumns()) {
					
					column.setTableId(oldTable.getId());
					dbManager.insertColumn(column);
					
					//检查列是否存在,如果不存在，创建列
					if(!coobi.exists(dbTable.getName(), column.getName())) {
						
						DbColumn dbColumn=new DbColumn();
						dbColumn.setComment(column.getComment());
						dbColumn.setDbType(column.getDbType());
						dbColumn.setEntityName(column.getEntityName());
						dbColumn.setLength(column.getLength());
						dbColumn.setName(column.getName());
						dbColumn.setNotNull(column.isNotNull());
						dbColumn.setPrimary(column.isPrimary());
						if(!coobi.createColumn(table.getName(), dbColumn)) {
							throw new RuntimeException("无法创建表["+table.getName()+"]的["+column.getName()+"]列。");
						}
					}
				}
				
				//删除索引配置
				dbManager.deleteIndexs(oldTable.getId());
				
				//新增索引配置
				for (org.scboy.dbms.api.entity.DbIndex index : dbTable.getIndexs()) {
					
					index.setTableId(oldTable.getId());
					dbManager.insertIndex(index);
					
					//检查索引是否存在,如果不存在，创建索引
					if(!coobi.existsIndex(dbTable.getName(), index.getIndexName())) {
						DbIndex dbIndex=new DbIndex();
						dbIndex.setColumns(index.getColumns());
						dbIndex.setIndexName(index.getIndexName());
						if(!coobi.createIndex(table.getName(), dbIndex)) {
							throw new RuntimeException("创建数据库表["+table.getName()+"]的["+index.getIndexName()+"]索引失败。");
						}
					}
				}
			}
		}
	}

	/**
	 * 初始化SQL配置
	 */
	@Transactional
	public void initSql() {
		//初始化数据库相关SQL
		List<SqlConfig> configs=new ArrayList<SqlConfig>();
		
		for (SQLConfig sqlConfig : SqlCache.getSqlConfigs()) {
			SqlConfig config=new SqlConfig();
			config.setName(sqlConfig.getName());
			config.setNote(sqlConfig.getNote());
			config.setServerName(properties.getServerName());
			config.setSqlId(sqlConfig.getId());
			
			SQLEntity sqlEntity=sqlConfig.getSqlEntity();
			org.scboy.dbms.api.entity.SQLEntity entity=new org.scboy.dbms.api.entity.SQLEntity();
			entity.setName(sqlEntity.getName());
			entity.setNote(sqlEntity.getNote());
			entity.setSql(sqlEntity.getSql());
			entity.setVersion(sqlEntity.getVersion());
			config.setSqlEntity(entity);
			configs.add(config);
		}
		dbManager.initSql(configs);
	}
	

	/**
	 * 初始化数据方法
	 */
	public void initData() {
		for (String  tablename : InitDataCache.initData.keySet()) {
			log.info("初始化["+tablename+"]表的数据");
			
			DbTable entity=DbTableCache.getDbTable(tablename);
			//获取主键
			Map<String, DbColumn> primaryKeys=new HashMap<String, DbColumn>();
			for (DbColumn column : entity.getColumns()) {
				if(column.isPrimary()){
					primaryKeys.put(column.getEntityName(), column);
				}
			}
			if(primaryKeys.size()==0){
				//表没有主键，无法初始化。不能检查数据重复，直接跳过。
				continue;
			}
			
			for (Model fileDataInput : InitDataCache.initData.get(tablename)) {
				List<Model> dataList=new Gson().fromJson(FileScan.readText((InputStream)fileDataInput.get("inputStream"), fileDataInput.getString("charset")),new TypeToken<List<Model>>(){}.getType());
				//检查数据是否存在。
				for (Model map : dataList) {
					
					//构建验重参数
					Model keyParam=Model.create();
					for (String primaryKey : primaryKeys.keySet()) {
						Object pk=map.get(primaryKey);
						if(pk==null){
							//数据没有主键的值，不能初始化。
							continue;
						}
						keyParam.add(primaryKey, pk);
					}
					
					if(coobi.executeGet(tablename, keyParam)==null){
						coobi.executeInsert(tablename, map);
					}
				}
			}
		}
	}
	
	@PreDestroy
	public void destroy() {
		timer.cancel();
	}
	
}
